home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Database / SimpleTableView-1 / FileTable.m < prev    next >
Text File  |  1995-06-12  |  8KB  |  321 lines

  1. // -------------------------------------------------------------------------------------
  2. //  FileTable
  3. //  This software is without warranty of any kind.  Use at your own risk.
  4. // -------------------------------------------------------------------------------------
  5.  
  6. #import <objc/objc.h>
  7. #import <appkit/appkit.h>
  8. #import <mach/mach.h>
  9. #import <dbkit/dbkit.h>
  10. #import <libc.h>
  11. #import <stdio.h>
  12. #import <string.h>
  13. #import <ctype.h>
  14. #import "FileTable.h"
  15.  
  16. // -------------------------------------------------------------------------------------
  17. @implementation FileTable
  18.  
  19. // -------------------------------------------------------------------------------------
  20.  
  21. /* count items in an unparsed table */
  22. static int dt_countList(char *list)
  23. {
  24.     int        c;
  25.     char    *r = list;
  26.     
  27.     /* make sure there is at least one entry */
  28.     if (!r) return 0;
  29.     while (isspace(*r)) r++;
  30.     if (!*r) return 0;
  31.     
  32.     /* count remaining entries */
  33.     for (c = 0, r = list; *r;) {
  34.         c++;
  35.         while (*r && !isspace(*r)) r++;    // skip value
  36.         while (isspace(*r)) r++;        // skip white
  37.     }
  38.     
  39.     return c;
  40. }
  41.  
  42. /* count items in a parsed table */
  43. static int dt_countTable(char **tbl)
  44. {
  45.     int    c;
  46.     if (!tbl) return 0;        // no entries counted
  47.     for (c = 0; tbl[c]; c++);
  48.     return c;
  49. }
  50.  
  51. /* parse a string table */
  52. static char **dt_parseTable(char *list)
  53. {
  54.     int        n, tblSize;
  55.     char    **theTbl, *r;
  56.  
  57.     /* skip whitespace in list */
  58.     if (!list) return (char**)NULL;
  59.     while (isspace(*list)) list++;
  60.     if (!*list) return (char**)NULL;
  61.  
  62.     /* count items in table */
  63.     tblSize = sizeof(char*) * (dt_countList(list) + 1); // include terminator
  64.     theTbl = (char**)malloc(tblSize);
  65.     memset(theTbl, 0, tblSize);
  66.     
  67.     /* fill table */
  68.     for (n = 0, r = list; *r;) {
  69.         theTbl[n++] = r;
  70.         while (*r && !isspace(*r)) r++;
  71.         while (isspace(*r)) *r++ = 0;
  72.     }
  73.     
  74.     /* return the table */
  75.     return theTbl;
  76.  
  77. }
  78.  
  79. // -------------------------------------------------------------------------------------
  80.  
  81. /* return access timestamp */
  82. - (time_t)timestamp
  83. {
  84.     struct stat        st;
  85.     if (stat((char*)tableHandle->access, &st) < 0) {
  86.         NXLogError("Unable to stat file %s", tableHandle->access);
  87.         return 0;
  88.     }
  89.     return st.st_mtime;
  90. }
  91.  
  92. // -------------------------------------------------------------------------------------
  93.  
  94. - _addSimpleColumn:(char*)title :(char)cType :(float)size
  95. {
  96.     int                t;
  97.     char            *types = CDT_TYPES;
  98.     dataColumn_t    *dc = (dataColumn_t*)malloc(sizeof(dataColumn_t));
  99.     memset(dc, 0, sizeof(dataColumn_t));
  100.     for (t = 0; types[t] && (types[t] != cType); t++);
  101.     dc->index        = [tableHandle->columnId count];
  102.     dc->type         = types[t]? t : CDT_UNKNOWN;
  103.     dc->keyTag       = NXCopyStringBuffer(title);
  104.     dc->title        = (char*)nil;
  105.     dc->size         = size;
  106.     dc->minSize      = 0.0;
  107.     dc->displayOrder = dc->index;
  108.     dc->alignment    = NX_CENTERED;
  109.     dc->isEditable   = dc->index? YES : NO;
  110.     dc->isHidden     = (dc->displayOrder >= 0)? NO : YES;
  111.     dc->nilValue     = (char*)nil;
  112.     [self addColumnInfo:dc];
  113.     return self;
  114. }
  115.  
  116. /* load table column info */
  117. - readTableColumns
  118. {
  119.     char        buf[2048];
  120.     BOOL        cLoaded = NO, vLoaded = NO;
  121.     FILE        *fh;
  122.     
  123.     /* open table */
  124.     if (!(fh = fopen(tableHandle->access, "r"))) {
  125.         NXLogError("Unable to open file %s", tableHandle->access);
  126.         return (id)nil;
  127.     }
  128.  
  129.     /* find/read column information */    
  130.     while (fgets(buf, sizeof(buf), fh) && (*buf == '#')) {
  131.         
  132.         /* view information */
  133.         if (!vLoaded && !strncmp(buf, "#!t", 3)) {
  134.             char viewName[128];
  135.             NXSize s = { 0.0, 0.0 };
  136.             sscanf(buf + 4, "%s %f %f", viewName, &s.width, &s.height);
  137.             tableHandle->viewSize = s;
  138.             vLoaded = YES;
  139.             continue;
  140.         }
  141.         
  142.         /* column information */
  143.         if (!cLoaded && (!strncmp(buf, "#!c\t", 4) || !strncmp(buf, "#\t", 2))) {
  144.             int key, numCols;
  145.             char **tbl, *b = buf + 1;
  146.             for (;*b && (*b != ' ') && (*b != '\t'); b++);
  147.             for (;(*b == ' ') || (*b == '\t'); b++);
  148.             if (!(tbl = dt_parseTable(b))) continue;
  149.             numCols = dt_countTable(tbl);
  150.             for (key = 0; key < numCols; key++) {
  151.                 float size = 0.0;
  152.                 char *title = tbl[key], *p = index(title, ':'), ctype[4];
  153.                 if (p) { *p++ = 0; sscanf(p, "%c:%f", ctype, &size); }
  154.                 [self _addSimpleColumn:title :*ctype :size];
  155.             }
  156.             free(tbl);
  157.             cLoaded = YES;
  158.             continue;
  159.         }
  160.         
  161.     }
  162.     
  163.     /* close table */
  164.     fclose(fh);
  165.     
  166.     return self;
  167. }
  168.  
  169. // -------------------------------------------------------------------------------------
  170.  
  171. /* load table */
  172. - readTableData
  173. {
  174.     FILE    *fh;
  175.     u_int    r;
  176.     char    buf[4096];
  177.     int        count = [tableHandle->columnId count];
  178.     
  179.     /* open table */
  180.     if (!tableHandle->access) return (id)nil;
  181.     if (!(fh = fopen((char*)tableHandle->access, "r"))) {
  182.         NXLogError("Unable to open file %s", tableHandle->access);
  183.         return (id)nil;
  184.     }
  185.     
  186.     /* read table data */
  187.     for (r = 0; fgets(buf, sizeof(buf), fh);) {
  188.         int c, tlen;
  189.         id rowId;
  190.         char **tbl;
  191.         if (!*buf || (*buf == '#') || (*buf == '\t') || (*buf == ' ')) continue;
  192.         if (!(tbl = dt_parseTable(buf))) continue;
  193.         rowId = [[List alloc] initCount:count];
  194.         [tableHandle->dataId insertObject:rowId at:r];
  195.         for (tlen = dt_countTable(tbl), c = 0; c < count; c++) {
  196.             char *val = (c < tlen)? tbl[c] : (char*)nil;
  197.             dataEntry_t *de = entryNEW;
  198.             de->value = (char*)[self copyStringValue:val forColumn:c];
  199.             de->isValid = -1;
  200.             [rowId insertObject:(id)de at:c];
  201.         }
  202.         free(tbl);
  203.         r++;
  204.     }
  205.     
  206.     /* close table */
  207.     fclose(fh);
  208.     
  209.     return self;
  210. }
  211.  
  212. // -------------------------------------------------------------------------------------
  213.  
  214. - writeTable
  215. {
  216.     int            r, c;
  217.     FILE        *fh;
  218.     struct stat    st;
  219.     
  220.     /* make backup copy */
  221.     if (stat(tableHandle->access, &st) >= 0) {
  222.         char bku[MAXPATHLEN + 1];
  223.         sprintf(bku, "%s~", tableHandle->access);
  224.         if (rename(tableHandle->access, bku)) {
  225.             NXLogError("Unable to rename file %s to %s", tableHandle->access, bku);
  226.             return (id)nil;
  227.         }
  228.     }
  229.     
  230.     /* open file for writing */
  231.     if (!(fh = fopen(tableHandle->access, "w+"))) {
  232.         NXLogError("Unable to open file %s", tableHandle->access);
  233.         return (id)nil;
  234.     }
  235.  
  236.     /* view header */
  237.     fprintf(fh, "#!t %s %.0f %.0f\n", tableHandle->name,
  238.         tableHandle->viewSize.width, tableHandle->viewSize.height);
  239.     
  240.     /* column headers */
  241.     fprintf(fh, "#!c\t");
  242.     for (c = 0; c < [tableHandle->columnId count]; c++) {
  243.         dataColumn_t *ci = [self columnInfoAt:c];
  244.         fprintf(fh, "%s:%c:%.0f\t", ci->keyTag, *(CDT_TYPES + ci->type), ci->size);
  245.     }
  246.     fprintf(fh, "\n");
  247.     
  248.     /* data */
  249.     for (r = 0; r < [tableHandle->dataId count]; r++) {
  250.         id    rowId = [tableHandle->dataId objectAt:r];
  251.         for (c = 0; c < [rowId count]; c++) {
  252.             dataColumn_t *ci = [self columnInfoAt:c];
  253.             dataEntry_t *de = entryPTR(rowId, c);
  254.             if (c) fprintf(fh, "\t");
  255.             fprintf(fh, "%s", (*de->value?de->value:(ci->nilValue?ci->nilValue:"*")));
  256.         }
  257.         fprintf(fh, "\n");
  258.     }
  259.     
  260.     /* close file */
  261.     fclose(fh);
  262.     
  263.     /* re-stat file to get modified date */
  264.     if (stat((char*)tableHandle->access, &st) >= 0) tableHandle->date = st.st_mtime;
  265.     else NXLogError("Unable to stat file %s", tableHandle->access);
  266.     
  267.     return self;
  268. }
  269.  
  270. // -------------------------------------------------------------------------------------
  271.  
  272. /* create value for column */
  273. - (const char*)copyStringValue:(const char*)value forColumn:(int)index
  274. {
  275.     char            *vcopy, buff[64];
  276.     dataColumn_t    *dc = [self columnInfoAt:index];
  277.     
  278.     /* check arguments */
  279.     if (!dc) return (char*)nil;
  280.     
  281.     /* special return types */
  282.     switch (dc->type) {
  283.         case CDT_BOOLEAN:
  284.             return NXCopyStringBuffer(((value && strcmp(value,"NO"))?"YES":"NO"));
  285.             break;
  286.         case CDT_INTEGER:
  287.             sprintf(buff, "%d", (value?atoi(value):0));
  288.             return NXCopyStringBuffer(buff);
  289.             break;
  290.         case CDT_STRING:
  291.         default:
  292.             if (!value) return NXCopyStringBuffer("");
  293.             for (vcopy = (char*)value; isspace(*vcopy); vcopy++); // skip white space
  294.             vcopy = strcpy((char*)malloc(strlen(vcopy) + 1), vcopy);
  295.             { char *b,*v; for (b=v=vcopy;*v;v++) if(!isspace(*v)){if(b!=v)*b=*v;b++;} *b=0; }
  296.             if (dc->nilValue && !strcmp(dc->nilValue, vcopy)) *vcopy = 0;
  297.             return (char*)realloc(vcopy, strlen(vcopy) + 1);
  298.             break;
  299.     }
  300.  
  301.     /* control should not reach here */
  302.     return NXCopyStringBuffer("");
  303. }
  304.  
  305. // -------------------------------------------------------------------------------------
  306.  
  307. /* validate specific value (intended for subclass implementation) */
  308. - (BOOL)verifyValue:(const char*)value dataType:(int)dataType
  309. {
  310.     if (!value) return NO;
  311.     switch (dataType) {
  312.         case CDT_STRING:
  313.         case CDT_INTEGER:
  314.         default:
  315.             return YES;
  316.     }
  317.     return YES;
  318. }
  319.  
  320. // -------------------------------------------------------------------------------------
  321. @end